package com.pengtu.service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipInputStream;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Session;
import org.jbpm.api.ExecutionService;
import org.jbpm.api.HistoryService;
import org.jbpm.api.ManagementService;
import org.jbpm.api.ProcessDefinition;
import org.jbpm.api.ProcessEngine;
import org.jbpm.api.ProcessInstance;
import org.jbpm.api.RepositoryService;
import org.jbpm.api.TaskService;
import org.jbpm.api.cmd.Command;
import org.jbpm.api.cmd.Environment;
import org.jbpm.api.history.HistoryDetailQuery;
import org.jbpm.api.history.HistoryProcessInstance;
import org.jbpm.api.history.HistoryTask;
import org.jbpm.api.model.ActivityCoordinates;
import org.jbpm.api.task.Participation;
import org.jbpm.api.task.Task;
import org.jbpm.pvm.internal.history.model.HistoryTaskInstanceImpl;
import org.jbpm.pvm.internal.model.ExecutionImpl;
import org.jbpm.pvm.internal.task.OpenTask;
import org.jbpm.pvm.internal.task.ParticipationImpl;
import org.jbpm.pvm.internal.task.SwimlaneImpl;
import org.jbpm.pvm.internal.task.TaskImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.pengtu.Constants;
import com.pengtu.dao.app.RoleDao;
import com.pengtu.entity.app.Role;
import com.pengtu.entity.app.User;
import com.pengtu.service.custom.*;
import com.pengtu.utils.jbpm.rollback.TaskDelegateCmd;

@Component
@Transactional
public class JbpmTemplateManager {
	private ProcessEngine processEngine;
	private RepositoryService repositoryService = null;
	private ExecutionService executionService = null;
	private TaskService taskService = null;
	private HistoryService historyService = null;
	private ManagementService managementService = null;

	private RoleDao roleDao;

	@Autowired
	public void setRoleDao(RoleDao roleDao) {
		this.roleDao = roleDao;
	}

	public JbpmTemplateManager() {

	}

	public JbpmTemplateManager(ProcessEngine processEngine) {
		this.processEngine = processEngine;
		repositoryService = processEngine.getRepositoryService();
		executionService = processEngine.getExecutionService();
		taskService = processEngine.getTaskService();
		historyService = processEngine.getHistoryService();
		managementService = processEngine.getManagementService();
	}

	public ProcessEngine getProcessEngine() {
		return processEngine;
	}

	public void setProcessEngine(ProcessEngine processEngine) {
		this.processEngine = processEngine;
		System.out.println(processEngine);
		repositoryService = processEngine.getRepositoryService();
		executionService = processEngine.getExecutionService();
		taskService = processEngine.getTaskService();
		historyService = processEngine.getHistoryService();
		managementService = processEngine.getManagementService();
	}

	public RepositoryService getRepositoryService() {
		return repositoryService;
	}

	public void setRepositoryService(RepositoryService repositoryService) {
		this.repositoryService = repositoryService;
	}

	public ExecutionService getExecutionService() {
		return executionService;
	}

	public void setExecutionService(ExecutionService executionService) {
		this.executionService = executionService;
	}

	public TaskService getTaskService() {
		return taskService;
	}

	public void setTaskService(TaskService taskService) {
		this.taskService = taskService;
	}

	public HistoryService getHistoryService() {
		return historyService;
	}

	public void setHistoryService(HistoryService historyService) {
		this.historyService = historyService;
	}

	public ManagementService getManagementService() {
		return managementService;
	}

	public void setManagementService(ManagementService managementService) {
		this.managementService = managementService;
	}

	// --------------------------------------------------------------------------------------

	/**
	 * 部署流程到数据库
	 */
	public String deploy(String resourceName) {
		ZipInputStream zis = new ZipInputStream(this.getClass()
				.getResourceAsStream(resourceName));
		return repositoryService.createDeployment()
				.addResourcesFromZipInputStream(zis).deploy();
	}

	/**
	 * 启动流程
	 * 
	 * @param deployId
	 * @param variables
	 * @return
	 */
	public String startProcess(String deployId, Map<String, Object> variables) {
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(deployId).uniqueResult();
		// 启动工作流
		ProcessInstance pi = executionService.startProcessInstanceById(
				pd.getId(), variables);

		assignTask(pi, pd);

		return pi.getId();
	}

	public void startProcess(String deployId, Map<String, Object> variables,
			String processDefinitionKey, String processKey) {
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.deploymentId(deployId).uniqueResult();
		// 启动工作流
		variables.put(Constants.START_TYPE, processDefinitionKey);
		ProcessInstance pi = executionService.startProcessInstanceById(
				pd.getId(), variables, processKey);

		assignTask(pi, pd);
	}

	/**
	 * 授权
	 * 
	 * @param pi
	 * @param pd
	 */
	public void assignTask(ProcessInstance pi, ProcessDefinition pd) {
		if (pd == null) {
			pd = repositoryService.createProcessDefinitionQuery()
					.processDefinitionId(pi.getProcessDefinitionId())
					.uniqueResult();
		}

		// 取得当前任务的名称，然后根据该任务名称以及流程定义，查看该任务将由哪一个用户或用户组来处理比较合适
		Iterator<String> activityNames = pi.findActiveActivityNames()
				.iterator();

		while (activityNames.hasNext()) {
			String actName = activityNames.next();
			List<Task> tasks = taskService.createTaskQuery()
					.processInstanceId(pi.getId()).activityName(actName).list();
			for (Task task : tasks) {
				List<Participation> participationList = taskService
						.getTaskParticipations(task.getId());
				for (Participation participation : participationList) {
					if (StringUtils.isNotEmpty(participation.getGroupId())) {
						String roleName = participation.getGroupId();
						String hql = "select vo from Role vo where vo.roleName=?";
						Object[] objs = { roleName };
						List<Role> roles = roleDao.find(hql, objs);
						for (Role role : roles) {
							taskService.addTaskParticipatingGroup(task.getId(),
									role.getId(), Participation.CANDIDATE);
						}
					}
					if (StringUtils.isNotEmpty(participation.getUserId())) {
						String userId = participation.getUserId();
						taskService.addTaskParticipatingUser(task.getId(),
								userId, Participation.CANDIDATE);
					}
				}
			}
		}

	}

	/**
	 * 根据key获取流程实例(这里我使用的uuid)
	 * 
	 * @param key
	 *            (对应于数据库表jbpm4_execution中的KEY_字段)
	 * @return 返回查找到得流程实例，没有返回null
	 */
	public ProcessInstance getProcessInstance(String key) {
		return executionService.createProcessInstanceQuery()
				.processInstanceKey(key).uniqueResult();
	}

	/**
	 * 根据任务ID查询任务 getTask: 适用:
	 * 
	 * @param taskId
	 * @return
	 * @exception
	 * @since 1.0.0
	 */
	public Task getTask(String taskId) {
		return taskService.getTask(taskId);
	}

	/**
	 * 查询待接任务列表
	 * 
	 * @param user
	 * @return
	 */
	public List<Task> findGroupTasks(User user) {
		List<Task> taskList = taskService.findGroupTasks(user.getId());
		return taskList;
	}

	/**
	 * 获取指定用户名字的任务
	 * 
	 * @param userId
	 * @return
	 */
	public List<Task> findPersonalTasks(String userId) {
		return taskService.findPersonalTasks(userId);
	}

	/**
	 * 接收任务
	 * 
	 * @param taskId
	 * @param userId
	 */
	public void takeTask(String taskId, String userId) {
		taskService.takeTask(taskId, userId);
	}

	/**
	 * 删除任务包括历史任务 deleteTaskCascade: 适用:
	 * 
	 * @param taskId
	 * @exception
	 * @since 1.0.0
	 */
	public void deleteTaskCascade(String taskId) {
		Task task = taskService.getTask(taskId);
		OpenTask openTask = (OpenTask) task;
		openTask.delete("no reason");
	}

	/**
	 * 删除任务 deleteTask: 适用:
	 * 
	 * @param taskId
	 * @exception
	 * @since 1.0.0
	 */
	public void deleteTask(String taskId) {
		taskService.deleteTask(taskId);
	}

	/**
	 * 删除该任务并说明理由 deleteTask: 适用:
	 * 
	 * @param taskId
	 * @param reason
	 * @exception
	 * @since 1.0.0
	 */
	public void deleteTask(String taskId, String reason) {
		taskService.deleteTask(taskId, reason);
	}

	/**
	 * 得到流程图的inputStream
	 * 
	 * @param deployId
	 * @param pngFileName
	 * @return
	 */
	public InputStream getInputStream(String deployId, String pngFileName) {
		return repositoryService.getResourceAsStream(deployId, pngFileName);
	}

	/**
	 * 根据业务ID得到流程图的InputStream
	 * 
	 * @param id
	 * @return
	 */
	public InputStream getInputStreamById(String id, String pngFileName) {
		ProcessInstance processInstance = executionService
				.createProcessInstanceQuery().processInstanceKey(id)
				.uniqueResult();
		ProcessDefinition processDefinition = null;
		if (processInstance == null) {
			HistoryProcessInstance historyprocessInstance = this.historyService
					.createHistoryProcessInstanceQuery().processInstanceKey(id)
					.uniqueResult();
			String processDefinitionId = historyprocessInstance
					.getProcessDefinitionId();
			processDefinition = repositoryService
					.createProcessDefinitionQuery()
					.processDefinitionId(processDefinitionId).uniqueResult();
		} else {
			String processDefinitionId = processInstance
					.getProcessDefinitionId();
			processDefinition = repositoryService
					.createProcessDefinitionQuery()
					.processDefinitionId(processDefinitionId).uniqueResult();
		}
		InputStream is = this.getInputStream(
				processDefinition.getDeploymentId(), pngFileName);
		return is;
	}

	/**
	 * 得到流程实例活动名
	 * 
	 * @param processInstance
	 * @return
	 */
	public ActivityCoordinates getAllActivityNames(
			ProcessInstance processInstance) {
		if (processInstance == null) {
			return null;
		}
		Set<String> activityNames = processInstance.findActiveActivityNames();
		ActivityCoordinates ac = repositoryService.getActivityCoordinates(
				processInstance.getProcessDefinitionId(), activityNames
						.iterator().next());
		return ac;
	}

	/**
	 * 得到执行人 getAssignName: 适用:
	 * 
	 * @param processInstance
	 * @return
	 * @exception
	 * @since 1.0.0
	 */
	public List<String> getAssignName(ProcessInstance processInstance) {
		Iterator<String> activityNames = null;
		try {
			activityNames = processInstance.findActiveActivityNames()
					.iterator();
		} catch (NullPointerException e) {
			// TODO: handle exception
			System.out.println(e);
			return null;
		}
		List<String> map = new ArrayList<String>();
		while (activityNames.hasNext()) {
			String actName = activityNames.next();
			List<Task> tasks = taskService.createTaskQuery()
					.processInstanceId(processInstance.getId())
					.activityName(actName).list();
			for (Task task : tasks) {
				TaskImpl taskImpl = (TaskImpl) task;
				if (StringUtils.isNotEmpty(taskImpl.getAssignee())) {
					map.add(taskImpl.getAssignee());
				} else if (null != taskImpl.getSwimlane()) {
					if (StringUtils.isNotEmpty(taskImpl.getSwimlane()
							.getAssignee())) {
						map.add(taskImpl.getSwimlane().getAssignee());
					} else {
						Set<ParticipationImpl> participations = ((SwimlaneImpl) taskImpl
								.getSwimlane()).getParticipations();
						for (ParticipationImpl participationImpl : participations) {
							if (StringUtils.isNotEmpty(participationImpl
									.getGroupId())) {
								if (com.pengtu.utils.StringUtils
										.hasChinese(participationImpl
												.getGroupId())) {
									map.add(participationImpl.getGroupId());
								}
							}
							if (StringUtils.isNotEmpty(participationImpl
									.getUserId())) {
								map.add(participationImpl.getUserId());
							}
						}
					}
				} else {
					List<Participation> participationList = taskService
							.getTaskParticipations(task.getId());
					for (Participation participation : participationList) {
						if (StringUtils.isNotEmpty(participation.getGroupId())) {
							if (com.pengtu.utils.StringUtils
									.hasChinese(participation.getGroupId())) {
								map.add(participation.getGroupId());
							}
						}
						if (StringUtils.isNotEmpty(participation.getUserId())) {
							map.add(participation.getUserId());
						}
					}
				}
			}
		}

		// ProcessDefinitionImpl processDefinitionImpl = (ProcessDefinitionImpl)
		// repositoryService
		// .createProcessDefinitionQuery()
		// .processDefinitionId(processInstance.getProcessDefinitionId())
		// .uniqueResult();
		// Iterator<String> activitysName = processInstance
		// .findActiveActivityNames().iterator();
		// System.out.println("#######"+activitysName.toString());
		// while (activitysName.hasNext()) {
		// String activityName = activitysName.next();
		// ActivityImpl activityImpl = processDefinitionImpl
		// .findActivity(activityName);
		// TaskDefinitionImpl taskDefinitionImpl =
		// processDefinitionImpl.getTaskDefinition(activityName);
		// System.out.println("&&&&&&&&"+taskDefinitionImpl);
		// }
		return map;
	}

	/**
	 * 得到所有流程定义
	 * 
	 * @return
	 */
	public List<ProcessDefinition> findProcessDefinition() {
		return repositoryService.createProcessDefinitionQuery().list();
	}

	/**
	 * 根据任务id获取指定变量值
	 * 
	 * @param taskId
	 * @param variableName
	 * @return
	 */
	public Object getVariableByTaskId(String taskId, String variableName) {
		return taskService.getVariable(taskId, variableName);
	}

	/**
	 * 提交任务
	 * 
	 * @param taskId
	 *            任务id
	 */
	public void completeTask(String taskId, Map<String, Object> variables) {
		TaskImpl task = (TaskImpl) taskService.getTask(taskId);
		ProcessInstance pi = executionService.createProcessInstanceQuery()
				.processInstanceId(task.getExecutionId()).uniqueResult();
		if (null == pi) {
			pi = task.getProcessInstance();
		}
		// 完成任务
		taskService.setVariables(taskId, variables);
		taskService.completeTask(taskId);
		if (null != pi) {
			assignTask(pi, null);
		}
	}

	/**
	 * 将任务流转到指定名字的流程中去
	 * 
	 * @param taskId
	 * @param outcome
	 */
	public void completeTask(String taskId, String outcome,
			Map<String, Object> variables) {
		Task task = taskService.getTask(taskId);
		ProcessInstance pi = executionService.createProcessInstanceQuery()
				.processInstanceId(task.getExecutionId()).uniqueResult();
		// 完成任务
		taskService.setVariables(taskId, variables);
		taskService.completeTask(taskId, outcome);

		// 为下一任务授权
		assignTask(pi, null);
	}

	/**
	 * 删除流程定义
	 * 
	 * @param deploymentId
	 */
	public void deteleProcessDefinition(String deploymentId) {
		repositoryService.deleteDeploymentCascade(deploymentId);
	}

	/**
	 * 设置参数 setVariables: 适用:
	 * 
	 * @param taskId
	 * @param variables
	 * @exception
	 * @since 1.0.0
	 */
	public void setVariables(String taskId, Map<String, Object> variables) {
		taskService.setVariables(taskId, variables);
	}

	/**
	 * 委派
	 * 
	 * @param taskId
	 * @param delegateUserId
	 */
	public void delegate(String taskId, String delegateUserId) {
		processEngine.execute(new TaskDelegateCmd(taskId, delegateUserId));
	}

	/**
	 * 根据taskId获得HistoryTaskInstanceImpl
	 * 
	 * @param taskId
	 * @return
	 */
	public HistoryTaskInstanceImpl getHistoryTaskInstanceByTaskId(
			final String taskId) {
		return processEngine.execute(new Command<HistoryTaskInstanceImpl>() {
			private static final long serialVersionUID = 1L;

			@Override
			public HistoryTaskInstanceImpl execute(Environment environment)
					throws Exception {
				Session session = environment.get(Session.class);
				StringBuilder hql = new StringBuilder();
				hql.append("select hti from ").append(
						HistoryTaskInstanceImpl.class.getName());
				hql.append(" as hti ");
				hql.append("where hti.historyTask.dbid = :taskDbid");
				return (HistoryTaskInstanceImpl) session
						.createQuery(hql.toString())
						.setLong("taskDbid", Long.valueOf(taskId))
						.uniqueResult();
			}
		});
	}

	/**
	 * 查询历史记录
	 * 
	 * @param taskId
	 * @return
	 */
	public HistoryDetailQuery getHistoryDetail(String taskId) {
		return historyService.createHistoryDetailQuery().taskId(taskId);
	}

	/**
	 * 根据业务id查询任务 getTaskById: 适用:
	 * 
	 * @param id
	 * @return
	 * @exception
	 * @since 1.0.0
	 */
	public TaskImpl getTaskById(final String id) {
		return processEngine.execute(new Command<TaskImpl>() {
			private static final long serialVersionUID = 1L;

			@Override
			public TaskImpl execute(Environment environment) throws Exception {
				Session session = environment.get(Session.class);
				StringBuilder hql = new StringBuilder();
				hql.append("select ts from ").append(TaskImpl.class.getName());
				hql.append(" as ts,").append(ExecutionImpl.class.getName());
				hql.append(" as ex ");
				hql.append("where ts.execution.id = ex.id and ex.key = :key");
				return (TaskImpl) session.createQuery(hql.toString())
						.setString("key", id).uniqueResult();
			}
		});
	}

	/**
	 * 根据 getHistoryTaskByAssign: 适用:
	 * 
	 * @param user
	 * @return
	 * @exception
	 * @since 1.0.0
	 */
	public List<HistoryTask> getHistoryTaskByAssign(final User user) {
		return historyService.createHistoryTaskQuery()
				.assignee(user.getUserName()).list();
	}

	/**
	 * 会签提交 isCounterSign: 适用:
	 * 
	 * @param joinsignTaskId
	 *            子任务id
	 * @param variables
	 *            下一任务变量
	 * @param conclusion
	 *            会签结果
	 * @param user
	 *            执行人
	 * @param flag
	 *            执行结果选择，是一票否决还是全部提交
	 * @exception
	 * @since 1.0.0
	 */
	public void isCounterSign(final String joinsignTaskId,
			final Map<String, Object> variables, String conclusion, User user,
			int flag) {
		TaskImpl subTask = (TaskImpl) taskService.getTask(joinsignTaskId);
		String superTaskId = subTask.getSuperTask().getId();
		CountersignInfo countersignInfo = (CountersignInfo) taskService
				.getVariable(superTaskId, "countersignInfo");
		countersignInfo.sign(user.getUserName(),
				Conclusion.getConclusion(conclusion));
		this.completeTask(joinsignTaskId, null);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("countersignInfo", countersignInfo);
		taskService.setVariables(superTaskId, map);
		if (flag == 1) {
			String leader = (String) taskService.getVariable(superTaskId,
					"leader");
			if (StringUtils.equals(leader, user.getUserName())) {
				this.completeTask(superTaskId, "片区负责人审批", variables);
				return;
			} else {
				if (countersignInfo.isAllSigned()) {
					variables.put("higher", leader);
					this.completeTask(superTaskId, "负责人审核", variables);
					return;
				} else {
					return;
				}
			}
		} else if (flag == 2) {
			if (countersignInfo.isAllSigned()) {
				this.completeTask(superTaskId, variables);
				return;
			} else {
				return;
			}
		}
	}

	public void isCounterSign(final String joinsignTaskId,
			final Map<String, Object> variables, String conclusion, User user) {
		TaskImpl subTask = (TaskImpl) taskService.getTask(joinsignTaskId);
		String superTaskId = subTask.getSuperTask().getId();
		CountersignInfo countersignInfo = (CountersignInfo) taskService
				.getVariable(superTaskId, "countersignInfo");
		countersignInfo.sign(user.getUserName(),
				Conclusion.getConclusion(conclusion));
		this.completeTask(joinsignTaskId, null);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("countersignInfo", countersignInfo);
		taskService.setVariables(superTaskId, map);
		if (countersignInfo.isAllSigned()) {
			this.completeTask(superTaskId, "同意", variables);
			return;
		} else {
			return;
		}
	}

	/**
	 * 判断是否是领导 isLeader: 适用:
	 * 
	 * @param taskId
	 * @param user
	 * @return
	 * @exception
	 * @since 1.0.0
	 */
	public boolean isLeader(String taskId, User user) {
		TaskImpl subTask = (TaskImpl) taskService.getTask(taskId);
		String superTaskId = subTask.getSuperTask().getId();
		String leader = (String) taskService.getVariable(superTaskId, "leader");
		if (StringUtils.isEmpty(leader)) {
			return false;
		}
		if (StringUtils.equals(leader, user.getUserName())) {
			return true;
		}
		return false;
	}

	/**
	 * 得到CountersignInfo getCountersignInfo: 适用:
	 * 
	 * @param subTaskId
	 * @return
	 * @exception
	 * @since 1.0.0
	 */
	public CountersignInfo getCountersignInfo(String subTaskId) {
		TaskImpl subTask = (TaskImpl) taskService.getTask(subTaskId);
		String superTaskId = subTask.getSuperTask().getId();
		return (CountersignInfo) taskService.getVariable(superTaskId,
				"countersignInfo");
	}
}
